1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package com.sun.media.sound;
26
27 import java.io.EOFException;
28 import java.io.IOException;
29 import java.io.InputStream;
30
31 import javax.sound.sampled.AudioFormat;
32 import javax.sound.sampled.AudioInputStream;
33
34
35
36
37
38
39 public class SoftJitterCorrector extends AudioInputStream {
40
41 private static class JitterStream extends InputStream {
42
43 static int MAX_BUFFER_SIZE = 1048576;
44 boolean active = true;
45 Thread thread;
46 AudioInputStream stream;
47
48 int writepos = 0;
49 int readpos = 0;
50 byte[][] buffers;
51 Object buffers_mutex = new Object();
52
53
54 int w_count = 1000;
55 int w_min_tol = 2;
56 int w_max_tol = 10;
57 int w = 0;
58 int w_min = -1;
59
60 int bbuffer_pos = 0;
61 int bbuffer_max = 0;
62 byte[] bbuffer = null;
63
64 public byte[] nextReadBuffer() {
65 synchronized (buffers_mutex) {
66 if (writepos > readpos) {
67 int w_m = writepos - readpos;
68 if (w_m < w_min)
69 w_min = w_m;
70
71 int buffpos = readpos;
72 readpos++;
73 return buffers[buffpos % buffers.length];
74 }
75 w_min = -1;
76 w = w_count - 1;
77 }
78 while (true) {
79 try {
80 Thread.sleep(1);
81 } catch (InterruptedException e) {
82
83 return null;
84 }
85 synchronized (buffers_mutex) {
86 if (writepos > readpos) {
87 w = 0;
88 w_min = -1;
89 w = w_count - 1;
90 int buffpos = readpos;
91 readpos++;
92 return buffers[buffpos % buffers.length];
93 }
94 }
95 }
96 }
97
98 public byte[] nextWriteBuffer() {
99 synchronized (buffers_mutex) {
100 return buffers[writepos % buffers.length];
101 }
102 }
103
104 public void commit() {
105 synchronized (buffers_mutex) {
106 writepos++;
107 if ((writepos - readpos) > buffers.length) {
108 int newsize = (writepos - readpos) + 10;
109 newsize = Math.max(buffers.length * 2, newsize);
110 buffers = new byte[newsize][buffers[0].length];
111 }
112 }
113 }
114
115 public JitterStream(AudioInputStream s, int buffersize,
116 int smallbuffersize) {
117 this.w_count = 10 * (buffersize / smallbuffersize);
118 if (w_count < 100)
119 w_count = 100;
120 this.buffers
121 = new byte[(buffersize/smallbuffersize)+10][smallbuffersize];
122 this.bbuffer_max = MAX_BUFFER_SIZE / smallbuffersize;
123 this.stream = s;
124
125
126 Runnable runnable = new Runnable() {
127
128 public void run() {
129 AudioFormat format = stream.getFormat();
130 int bufflen = buffers[0].length;
131 int frames = bufflen / format.getFrameSize();
132 long nanos = (long) (frames * 1000000000.0
133 / format.getSampleRate());
134 long now = System.nanoTime();
135 long next = now + nanos;
136 int correction = 0;
137 while (true) {
138 synchronized (JitterStream.this) {
139 if (!active)
140 break;
141 }
142 int curbuffsize;
143 synchronized (buffers) {
144 curbuffsize = writepos - readpos;
145 if (correction == 0) {
146 w++;
147 if (w_min != Integer.MAX_VALUE) {
148 if (w == w_count) {
149 correction = 0;
150 if (w_min < w_min_tol) {
151 correction = (w_min_tol + w_max_tol)
152 / 2 - w_min;
153 }
154 if (w_min > w_max_tol) {
155 correction = (w_min_tol + w_max_tol)
156 / 2 - w_min;
157 }
158 w = 0;
159 w_min = Integer.MAX_VALUE;
160 }
161 }
162 }
163 }
164 while (curbuffsize > bbuffer_max) {
165 synchronized (buffers) {
166 curbuffsize = writepos - readpos;
167 }
168 synchronized (JitterStream.this) {
169 if (!active)
170 break;
171 }
172 try {
173 Thread.sleep(1);
174 } catch (InterruptedException e) {
175
176 }
177 }
178
179 if (correction < 0)
180 correction++;
181 else {
182 byte[] buff = nextWriteBuffer();
183 try {
184 int n = 0;
185 while (n != buff.length) {
186 int s = stream.read(buff, n, buff.length
187 - n);
188 if (s < 0)
189 throw new EOFException();
190 if (s == 0)
191 Thread.yield();
192 n += s;
193 }
194 } catch (IOException e1) {
195
196 }
197 commit();
198 }
199
200 if (correction > 0) {
201 correction--;
202 next = System.nanoTime() + nanos;
203 continue;
204 }
205 long wait = next - System.nanoTime();
206 if (wait > 0) {
207 try {
208 Thread.sleep(wait / 1000000L);
209 } catch (InterruptedException e) {
210
211 }
212 }
213 next += nanos;
214 }
215 }
216 };
217
218 thread = new Thread(runnable);
219 thread.setDaemon(true);
220 thread.setPriority(Thread.MAX_PRIORITY);
221 thread.start();
222 }
223
224 public void close() throws IOException {
225 synchronized (this) {
226 active = false;
227 }
228 try {
229 thread.join();
230 } catch (InterruptedException e) {
231
232 }
233 stream.close();
234 }
235
236 public int read() throws IOException {
237 byte[] b = new byte[1];
238 if (read(b) == -1)
239 return -1;
240 return b[0] & 0xFF;
241 }
242
243 public void fillBuffer() {
244 bbuffer = nextReadBuffer();
245 bbuffer_pos = 0;
246 }
247
248 public int read(byte[] b, int off, int len) {
249 if (bbuffer == null)
250 fillBuffer();
251 int bbuffer_len = bbuffer.length;
252 int offlen = off + len;
253 while (off < offlen) {
254 if (available() == 0)
255 fillBuffer();
256 else {
257 byte[] bbuffer = this.bbuffer;
258 int bbuffer_pos = this.bbuffer_pos;
259 while (off < offlen && bbuffer_pos < bbuffer_len)
260 b[off++] = bbuffer[bbuffer_pos++];
261 this.bbuffer_pos = bbuffer_pos;
262 }
263 }
264 return len;
265 }
266
267 public int available() {
268 return bbuffer.length - bbuffer_pos;
269 }
270 }
271
272 public SoftJitterCorrector(AudioInputStream stream, int buffersize,
273 int smallbuffersize) {
274 super(new JitterStream(stream, buffersize, smallbuffersize),
275 stream.getFormat(), stream.getFrameLength());
276 }
277 }